package model.turtle;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import util.Location;
import util.Pixmap;
import util.Sprite;
import util.Vector;


/**
 * An observable extension of the sprite class.
 * Interaction occurs through the Commandable interface.
 * 
 * @author Matthew Roy
 */
public class Turtle extends Sprite implements Commandable {
    /**
     * default image for the turtle.
     */
    public static final Pixmap DEFAULT_ICON_IMAGE = new Pixmap("turtle.png");
    /**
     * default starting location for the turtle.
     */
    public static final Location DEFAULT_LOCATION = new Location(300, 250);
    /**
     * default size of the turtle.
     */
    public static final Dimension DEFAULT_SIZE = new Dimension(30, 30);
    /**
     * turtle angle must be between 0 and
     * the DEGREES_MODIFIER.
     */
    public static final int DEGREES_MODIFIER = 360;

    /**
     * Success constant.
     */
    public static final int SUCCESS = 1;
    /**
     * Fail constant.
     */
    public static final int FAIL = 0;
    /**
     * id number of the turtle.
     */
    private int myID;
    /**
     * path of the turtle.
     */
    private Path myPath;
    /**
     * direction of the turtle in degrees.
     */
    private int myAngle;
    /**
     * active state of the turtle (active if true).
     */
    private boolean myActiveState;
    /**
     * visible state of the turtle (visible if true).
     */
    private boolean myVisibleState;
    /**
     * current pen color of the turtle.
     */
    private Color myPenColor;
    /**
     * current pen color index of the turtle.
     */
    private int myPenColorIndex;
    /**
     * current pen width of the turtle.
     */
    private int myPenWidth;
    /**
     * current pen design of the turtle.
     */
    private String myPenDesign;
    /**
     * state of the pen (true if down).
     */
    private boolean myPenState;

    /**
     * Constructor.
     * 
     * @param image 
     * @param center 
     * @param size 
     * @param id 
     */
    public Turtle (Pixmap image, Location center,
                   Dimension size, int id) {
        super(image, center, size);
        myPath = new Path();
        myAngle = 0;
        myVisibleState = true;
        myPenState = true;
        myPenColor = Color.BLACK;
        myPenColorIndex = 0;
        myPenWidth = 1;
        myPenDesign = Commandable.SOLID_LINE_PATTERN;
        myActiveState = true;
        myID = id;
    }

    /**
     * default constructor.
     */
    public Turtle () {
        super(DEFAULT_ICON_IMAGE, DEFAULT_LOCATION, DEFAULT_SIZE);
        myAngle = 0;
    }

    /**
     * Clears the stamps from the turtle's path.
     */
    @Override
    public int clearStamps () {
        myPath.clearStamps();
        return SUCCESS;
    }

    @Override
    public void clear () {
        super.reset();
        myPath.clearPath();
    }

    /**
     * Gets the direction of the turtle.
     * 
     * @return the angle
     */
    @Override
    public int getHeading () {
        return myAngle;
    }

    /**
     * Gets the turtle ID.
     * 
     * @return the turtle's ID.
     */
    @Override
    public int getID () {
        return myID;
    }

    /**
     * Gets the image of the turtle.
     * 
     * @return the image
     */
    @Override
    public Pixmap getImage () {
        return super.getView();
    }

    /**
     * Gets the pen color.
     * 
     * @return the Color
     */
    @Override
    public int getPenColor () {
        return myPenColorIndex;
    }

    /**
     * Gets the state of the pen.
     * 
     * @return 1 if down, 0 if up
     */
    @Override
    public int getPenState () {
        if (myPenState) { return 1; }
        return 0;
    }

    /**
     * Gets the state of the turtle's visibilty.
     * 
     * @return 1 if visible, 0 if not visible
     */
    @Override
    public int getVisibility () {
        if (myVisibleState) { return 1; }
        return 0;
    }

    /**
     * Gets the x-coordinate.
     * 
     * @return the x-coordinate
     */
    @Override
    public int getXCoordinate () {
        return (int) getX();
    }

    /**
     * Gets the y-coordinate.
     * 
     * @return the y-coordinate
     */
    @Override
    public int getYCoordinate () {
        return (int) getY();
    }

    /**
     * Gets the active state.
     * 
     * @return whether the turtle is active or not.
     */
    @Override
    public int isActive () {
        if (myActiveState) { return SUCCESS; }
        return FAIL;
    }

    /**
     * Sets the turtle to invisible.
     */
    @Override
    public int makeInvisible () {
        myVisibleState = false;
        return SUCCESS;
    }

    /**
     * Sets the turtle to visible.
     */
    @Override
    public int makeVisible () {
        myVisibleState = true;
        return SUCCESS;
    }

    /**
     * Moves the turtle the pixels specified.
     * 
     * @param pixels is the number of pixels to move
     */
    @Override
    public int move (int pixels) {
        Point2D.Double start = new Point2D.Double(getX(), getY());
        translate(new Vector(myAngle, pixels));
        Point2D.Double newPosition = new Point2D.Double(getX(), getY());
        myPath.addNewSegment(start, newPosition, myPenColor,
                             myPenWidth, myPenDesign, myPenState);
        return pixels;
    }

    /**
     * Paints the turtle.
     * 
     * @param pen graphics
     */
    @Override
    public int paint (Graphics pen) {
        myPath.paint(pen);
        if (myVisibleState) {
            super.paint((Graphics2D) pen, myAngle);
        }
        return SUCCESS;
    }

    /**
     * paints the highlight box is turtle is active
     * @param pen graphics
     */
    public int paintHighlight (Graphics pen) {
        if (myActiveState) {
            pen.setColor(Color.RED);
            pen.drawRect((int) getLeft(), (int) getTop(), (int) getWidth(), (int) getHeight());
            return 1;
        }
        return 0;
    }

    /**
     * Turns the turtle the specified degrees.
     * 
     * @param degrees to rotate
     */
    @Override
    public int rotate (int degrees) {
        myAngle = (myAngle + degrees) % DEGREES_MODIFIER;
        return degrees;
    }

    /**
     * Sets the direction to the specified heading.
     * 
     * @param heading to point to
     */
    @Override
    public int setHeading (int heading) {
        myAngle = heading % DEGREES_MODIFIER;
        return myAngle;
    }

    /**
     * Sets the direction of a turtle towards a specific point
     * @param x dir
     * @param y dir
     */
    @Override
    public int setTowards (int x, int y) {
        final double DEGREES = 180.0;
        double deltaX = x - getXCoordinate();
        double deltaY = y - getYCoordinate();
        double newHeading = Math.atan(deltaY / deltaX) * DEGREES / Math.PI;
        setHeading((int) newHeading);
        return SUCCESS;
    }

    @Override
    public int setImage (Pixmap image) {
        super.setView(image);
        return SUCCESS;
    }

    @Override
    public int setActiveState (boolean stateOn) {
        myActiveState = stateOn;
        return stateOn ? 1 : 0;
    }

    /**
     * Sets the pen's color to the given Color.
     * @param color to change
     */
    public int setPenColor (Color color) {
        myPenColor = color;
        return SUCCESS;
    }

    /**
     * Sets the pen design to the given design.
     * 
     * @param design to change to
     */
    @Override
    public int setPenDesign (String design) {
        myPenDesign = design;
        return SUCCESS;
    }

    /**
     * Sets the pen state to down.
     */
    @Override
    public int setPenDown () {
        myPenState = true;
        return SUCCESS;
    }

    /**
     * Sets the pen state to up.
     */
    @Override
    public int setPenUp () {
        myPenState = false;
        return SUCCESS;
    }

    /**
     * sets the pen width to the given width.
     * 
     * @param penWidth size in pixels
     */
    public int setPenSize (int penWidth) {
        myPenWidth = penWidth;
        return SUCCESS;
    }

    /**
     * sets the position to the given position.
     * 
     * @param x is the desired x coordinate
     * @param y is the desired y coordinate
     */
    @Override
    public int setPosition (int x, int y) {
        setCenter(x, y);
        return SUCCESS;
    }

    /**
     * stamps an image of the turtle onto its path.
     */
    @Override
    public int stamp () {
        Point2D.Double start = new Point2D.Double(getX(), getY());
        myPath.addNewSegment(getImage(), getSize(), start, myAngle);
        return SUCCESS;
    }
}
